﻿using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;

namespace Songhay
{
    /// <summary>
    /// Static members for type handling.
    /// </summary>
    public static partial class FrameworkTypeUtility
    {
        /// <summary>
        /// Returns <c>true</c> when the specified value
        /// is an empty array, not an array or null.
        /// </summary>
        /// <param name="frameworkValue">The specified value.</param>
        public static bool ArrayIsNullOrEmptyOrNotArray(object frameworkValue)
        {
            if(frameworkValue == null) return true;

            Array array = frameworkValue as Array;
            return ((array == null) || (array.Length == 0));
        }

        /// <summary>
        /// Converts Unix time stamps
        /// to <see cref="System.DateTime"/>.
        /// </summary>
        /// <param name="time">A <see cref="System.Double"/>.</param>
        /// <returns>A <see cref="System.DateTime"/>.</returns>
        public static DateTime DateTimeFromUnixTime(double time)
        {
            return (new DateTime(1970, 1, 1, 0, 0, 0)).AddSeconds(time);
        }

        /// <summary>
        /// Converts the specified <see cref="DateTime "/> to RFC3339.
        /// </summary>
        /// <param name="utcDateTime">The UTC date and time.</param>
        public static string DateTimeToRfc3339DateTime(DateTime utcDateTime)
        {
            DateTimeFormatInfo dateTimeFormat = CultureInfo.InvariantCulture.DateTimeFormat;
            if(utcDateTime.Kind == DateTimeKind.Local)
            {
                return utcDateTime.ToString("yyyy'-'MM'-'dd'T'HH:mm:ss.ffzzz", dateTimeFormat);
            }
            return utcDateTime.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss.ff'Z'", dateTimeFormat);
        }

        /// <summary>
        /// Converts the specified <see cref="DateTime "/> to RFC822.
        /// </summary>
        /// <param name="dateTime">The date and time.</param>
        /// <remarks>
        /// “Shortcomings of OPML… The RFC 822 date format is considered obsolete,
        /// and amongst other things permits the representation of years as two digits.
        /// (RFC 822 has been superseded by RFC 2822.)
        /// In general, date and time formats should be represented according to RFC 3339.”
        /// http://www.answers.com/topic/opml
        /// </remarks>
        public static string DateTimeToRfc822DateTime(DateTime dateTime)
        {
            DateTimeFormatInfo dateTimeFormat = CultureInfo.InvariantCulture.DateTimeFormat;
            return dateTime.ToString(dateTimeFormat.RFC1123Pattern, dateTimeFormat);
        }

        /// <summary>
        /// Converts the current time
        /// to a Unix time stamp.
        /// </summary>
        /// <returns>A <see cref="System.Double"/>.</returns>
        public static double DateTimeToUnixTime()
        {
            return (DateTime.UtcNow
                - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
        }

        /// <summary>
        /// Converts a <see cref="System.DateTime"/>
        /// to a Unix time stamp.
        /// </summary>
        /// <param name="dateValue">The <see cref="System.DateTime"/>.</param>
        /// <returns>A <see cref="System.Double"/>.</returns>
        public static double DateTimeToUnixTime(DateTime dateValue)
        {
            return (dateValue.ToUniversalTime()
                - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
        }

        /// <summary>
        /// Converts inches as a <see cref="System.Single"/>
        /// to points.
        /// </summary>
        /// <param name="inches">The inches.</param>
        /// <remarks>
        /// 1 point = 0.013837 inch
        /// </remarks>
        public static float InchesToPoints(float inches)
        {
            return inches / 0.01387f;
        }

        /// <summary>
        /// Returns <c>true</c> when the specified value
        /// is <c>null</c> or <see cref="System.string.Empty"/>.
        /// </summary>
        /// <param name="frameworkValue">The framework value.</param>
        public static bool ObjectIsNullOrEmptyString(object frameworkValue)
        {
            if(frameworkValue == null) return true;
            else return string.IsNullOrEmpty(frameworkValue.ToString());
        }

        /// <summary>
        /// Converts points as a <see cref="System.Single"/>
        /// to inches.
        /// </summary>
        /// <param name="points">The points.</param>
        /// <remarks>
        /// 1 point = 0.013837 inch
        /// </remarks>
        public static float PointsToInches(float points)
        {
            return points * 0.01387f;
        }

        /// <summary>
        /// Sets the properties of the output <see cref="System.Type"/>.
        /// </summary>
        /// <param name="input">the input.</param>
        /// <param name="output">the output.</param>
        public static void SetProperties<TIn, TOut>(TIn input, TOut output)
            where TIn : class
            where TOut : class
        {
            if((input == null) || (output == null)) return;
            Type inType = input.GetType();
            Type outType = output.GetType();
            foreach(PropertyInfo info in inType.GetProperties())
            {
                PropertyInfo outfo = ((info != null) && info.CanRead)
                    ? outType.GetProperty(info.Name, info.PropertyType)
                    : null;
                if(outfo != null
                    && outfo.CanWrite
                    && (outfo.PropertyType.Equals(info.PropertyType)))
                    outfo.SetValue(output, info.GetValue(input, null), null);
            }
        }

        /// <summary>
        /// Returns the conventional database null
        /// (<see cref="System.DBNull"/>)
        /// for Microsoft SQL Server systems.
        /// </summary>
        /// <returns><see cref="System.DBNull"/></returns>
        public static DBNull SqlDatabaseNull()
        {
            return DBNull.Value;
        }
    }
}
